#!/usr/bin/env python3
""" SoLoud Beef (bf) wrapper generator """

import soloud_codegen

fo = open("../glue/soloud.bf", "w")


C_TO_BF_TYPES = {
    "string":"String",
    "int":"int32",
    "void":"void",
    "const char *":"char8 *",
    "char *":"char8 *",
    "unsigned int":"uint32",
    "float":"float",
    "double":"double",
    "float *":"float *",
    "File *":"void *",
    "unsigned char *":"uint8 *",
    "const unsigned char *":"uint8 *",
    "unsigned char":"uint8",
    "short *":"uint16 *"
}

for soloud_type in soloud_codegen.soloud_type:
    C_TO_BF_TYPES[soloud_type + " *"] = "SoloudObject"


def has_ex_variant(funcname):
    """ Checks if this function has an "Ex" variant """    
    if funcname[-2::] == "Ex":
        # Already an Ex..
        return False
    for func in soloud_codegen.soloud_func:
        if func[1] == (funcname + "Ex"):
            return True
    return False

fo.write("""
// SoLoud wrapper for Beef (bf)
// This file is autogenerated; any changes will be overwritten

using System;

namespace SoLoud
{

public class SoloudObject
{
    public void* objhandle;
}

""")

fo.write("\n")
#################################################################

def fix_default_param(defparam, classname):
    """ 'fixes' default parameters from C to what python expectes """
    if defparam == "false":
        return "0"
    if defparam == "true":
        return "1"
    if (classname + '::') == defparam[0:len(classname)+2:]:
        return defparam[len(classname)+2::]
    if 'Soloud::' in defparam:
        return "Soloud." + defparam[len("Soloud")+2::]
    return defparam

def external_pointer_fix(param):
    if param == "SoloudObject":
        return "void *"
    if param == "string":
        return "char8 *"
    return param

for x in soloud_codegen.soloud_type:
    first = True
    for y in soloud_codegen.soloud_func:
        if (x + "_") == y[1][0:len(x)+1:]:
            if first:
                fo.write('\n')
                fo.write('public class %s : SoloudObject\n{\n'%(x))
                for z in soloud_codegen.soloud_enum:
                    if z[0:len(x)+1] == x.upper()+'_':
                        s = str(soloud_codegen.soloud_enum[z])
                        fo.write('\tpublic const int %s = %s;\n'%(z[len(x)+1::], s))
                fo.write('\n\t[LinkName(\"%s_create\")]\n\tprivate static extern void* create();\n'%(x))
                fo.write('\tpublic this()\n\t{\n')
                fo.write('\t\tobjhandle = create();\n')
                fo.write('\t}\n')
                
                fo.write('\n\t[LinkName(\"%s_destroy\")]\n\tprivate static extern void* destroy(void* aObjHandle);\n'%(x))                
                fo.write('\tpublic ~this()\n\t{\n')
                fo.write('\t\tdestroy(objhandle);\n')
                fo.write('\t}\n')
                
                first = False
            funcname = y[1][len(x)+1::]
            # If the function has the name "Ex", remove the subfix
            if funcname[-2::] == "Ex":
                funcname = funcname[:len(funcname)-2]
            # Skip generating functions that have an Ex variant            
            if funcname == "create" or funcname == "destroy" or has_ex_variant(y[1]):
                pass # omit create/destroy, handled by __exit__ / close
            else:
                charptr = False
                floatptr = False
                ret = C_TO_BF_TYPES[y[0]]
                if y[0] == 'const char *':                    
                    charptr = True
                    ret = 'char8 *'
                if y[0] == 'const unsigned char *':                    
                    charptr = True
                    ret = 'uint8 *'
                if y[0] == 'float *':
                    floatptr = True
                    ret = 'float *'
                fo.write('\n\t[CLink]\n\tprivate static extern %s %s(void* aObjHandle'%(ret, y[1]))
                for z in y[2]:
                    if len(z) > 1:
                        if z[1] == 'a'+x:
                            pass # skip the 'self' pointer
                        else:
                            fo.write(', ')
                            fo.write(external_pointer_fix(C_TO_BF_TYPES[z[0]]) + ' ' + z[1])
                fo.write(');\n')
                
                fo.write('\tpublic %s %s('%(C_TO_BF_TYPES[y[0]], funcname))
                firstparm = True
                for z in y[2]:
                    if len(z) > 1:
                        if z[1] == 'a'+x:
                            pass # skip the 'self' pointer
                        else:
                            if firstparm:
                                firstparm = False
                            else:
                                fo.write(', ')
                            fo.write(C_TO_BF_TYPES[z[0]] + ' ' + z[1])
                            if len(z) > 2:
                                fo.write(' = ' + fix_default_param(z[2], x))
                fo.write(')\n\t{\n')
                fo.write('\t\t')
                if y[0] == 'void':
                    pass
                elif charptr:
                    fo.write('return ')
                elif floatptr:
                    fo.write('return ')
                else:
                    fo.write('return ')
                fo.write(y[1] + '(objhandle')
                for z in y[2]:
                    if len(z) > 1:
                        if z[1] == 'a'+x:
                            pass # skip the 'self' pointer
                        else:
                            fo.write(', ')
                            fudged_type = C_TO_BF_TYPES[z[0]]
                            if fudged_type == 'SoloudObject':
                                fo.write(z[1] + '.objhandle')
                            else:
                                fo.write(z[1])
                fo.write(');\n')
                fo.write('\t}\n')
    if not first:
        fo.write('}\n')

fo.write('}\n')

print("soloud.bf generated")

fo.close()
